﻿// The MIT License (MIT)
// Copyright (c) 2014 LuoZhihui
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System.Text;

namespace EasyTelnet
{
    /*
The Network Virtual Terminal
Communication is established using TCP/IP and is based on a Network Virtual Terminal (NVT). On the client, the Telnet program is responsible for translating incoming NVT codes to codes understood by the client's display device as well as for translating client-generated keyboard codes into outgoing NVT codes.
The NVT uses 7-bit codes for characters. The display device, referred to as a printer in the RFC, is only required to display the standard printing ASCII characters represented by 7-bit codes and to recognize and process certain control codes. The 7-bit characters are transmitted as 8-bit bytes with the most significant bit set to zero. An end-of-line is transmitted as a carriage return (CR) followed by a line feed (LF). If you want to transmit an actual carriage return, this is transmitted as a carriage return followed by a NUL (all bits zero) character. 

NVT ASCII is used by many other Internet protocols like SMTP and FTP.
The following control codes are required to be understood by the NVT. 

Name	                Code	Decimal Value	Function
NULL	                NUL	        0	                No operation
Line Feed	            LF	            10	                Moves the printer to the next print line, keeping the same horizontal position.
Carriage Return	CR	        13	                Moves the printer to the left margin of the current line.


The following further control codes are optional but should have the indicated defined effect on the display. 

Name	                Code	Decimal Value	Function
BELL	                    BEL	        7	                Produces an audible or visible signal (which does NOT move the print head.
Back Space	        BS	            8	                Moves the print head one character position towards the left margin. (On a printing device, this mechanism was commonly used to form composite characters by printing two basic characters on top of each other.)
Horizontal Tab	    HT	        9	                Moves the printer to the next horizontal tab stop. It remains unspecified how either party determines or establishes where such tab stops are located.
Vertical Tab	        VT	        11	                Moves the printer to the next vertical tab stop. It remains unspecified how either party determines or establishes where such tab stops are located.
Form Feed	        FF          	12	                Moves the printer to the top of the next page, keeping the same horizontal position. (On visual displays, this commonly clears the screen and moves the cursor to the top left corner.)
The NVT keyboard is specified as being capable of generating all 128 ASCII codes by using keys, key combinations, or key sequences.
*/

    /// <summary>
    ///     网络虚拟终端
    /// </summary>
    public class NetVirtualTerminal
    {
        /// <summary>
        /// 网络虚拟终端构造函数
        /// </summary>
        /// <param name="terminalPlatform">终端操作系统平台</param>
        /// <param name="width">终端宽</param>
        /// <param name="height">终端高</param>
        /// <param name="terminalType">终端类别</param>
        /// <param name="terminalEncoding">终端使用的字符编码</param>
        public NetVirtualTerminal(TerminalPlatform terminalPlatform, int width, int height, TerminalType terminalType, Encoding terminalEncoding)
            :this(terminalPlatform,width,height,terminalType,terminalEncoding,"quit")
        {
        }

        /// <summary>
        /// 网络虚拟终端构造函数
        /// </summary>
        /// <param name="terminalPlatform">终端操作系统平台</param>
        /// <param name="width">终端宽</param>
        /// <param name="height">终端高</param>
        /// <param name="terminalType">终端类别</param>
        /// <param name="terminalEncoding">终端使用的字符编码</param>
        /// <param name="quitCommand">退出指令</param>
        public NetVirtualTerminal(TerminalPlatform terminalPlatform, int width, int height, TerminalType terminalType, Encoding terminalEncoding,string quitCommand)
        {
            Platform = terminalPlatform;
            TerminalSize = new TerminalSize { Width = width, Height = height };
            TerminalType = new TerminalTypeWrapper(terminalType, terminalEncoding);
            TerminalEncoding = terminalEncoding;
            QuitCommand = quitCommand;
        }
        
        #region 属性

        /// <summary>
        /// 退出终端指令
        /// </summary>
        public string QuitCommand
        {
            get;
            set;
        }

        /// <summary>
        /// 终端字符编码
        /// </summary>
        public Encoding TerminalEncoding { get; set; }
        
        /// <summary>
        ///     当前平台行结束符
        /// </summary>
        public string EndOfLine
        {
            get
            {
                switch (Platform)
                {
                    case TerminalPlatform.Windows:
                        return "\r\n"; //CRLF
                    case TerminalPlatform.Unix:
                        return "\n"; //LF
                    case TerminalPlatform.Macintosh:
                        return "\r"; //CR
                    default:
                        return "\r";
                }
            }
        }

        public byte[] BytesEndOfLine
        {
            get
            {
                switch (Platform)
                {
                    case TerminalPlatform.Unix:
                        return new[] {  TerminalCommand.LF.ByteValue() }; //LF
                    case TerminalPlatform.Macintosh:
                        return new [] { TerminalCommand.CR.ByteValue() }; //CR
                    default:
                        return new [] { TerminalCommand.CR.ByteValue(), TerminalCommand.LF.ByteValue() }; //CRLF
                }
            }
        }

        /// <summary>
        ///     终端操作系统平台
        /// </summary>
        public TerminalPlatform Platform { get; set; }

        /// <summary>
        ///     终端尺寸
        /// </summary>
        public TerminalSize TerminalSize { get; set; }

        /// <summary>
        ///     终端类别
        /// </summary>
        public TerminalTypeWrapper TerminalType { get; set; }

        #endregion
    }

    #region 定义 终端目标平台、终端类别、终端控制字符

    /// <summary>
    ///     终端目标平台
    /// </summary>
    public enum TerminalPlatform
    {
        /// <summary>
        ///     Windows 系列服务器
        /// </summary>
        Windows,

        /// <summary>
        ///     Linux 和 Unix 系列服务器，大部分路由器都是用的Linux内核
        /// </summary>
        Unix,

        /// <summary>
        ///     苹果麦金塔系统服务器
        /// </summary>
        Macintosh
    }

    /// <summary>
    ///     终端类别
    /// </summary>
    public enum TerminalType
    {
        NVT_VT100,
        NVT_ANSI,
        NVT_TTY
    }

    /// <summary>
    ///     网络虚拟终端控制字符
    ///     <para>前面的字符对网络虚拟终端具有特别的意义（尤其是128个基本ASCII字符和128个扩展ASCII字符）。</para>
    ///     <para>下面的字符可能在虚拟终端上有意义，但不一定使用</para>
    /// </summary>
    internal enum TerminalCommand : byte
    {
        /// <summary>
        ///     无操作
        /// </summary>
        NULL = 0,

        /// <summary>
        ///     发出声响或可见的信号
        /// </summary>
        BEL = 7,

        /// <summary>
        ///     移向左边距
        /// </summary>
        BS = 8,

        /// <summary>
        ///     水平制表符
        /// </summary>
        HT = 9,

        /// <summary>
        ///     换行 (UNIX/Linux)  /n
        /// </summary>
        LF = 10,

        /// <summary>
        ///     垂直制表符
        /// </summary>
        VT = 11,

        /// <summary>
        ///     换页符
        /// </summary>
        FF = 12,

        /// <summary>
        ///     回车 (MACINTOSH) /r
        /// </summary>
        CR = 13,
    }

    #endregion

    #region  定义  终端尺寸、终端类别包装

    /// <summary>
    ///     终端尺寸
    /// </summary>
    public class TerminalSize
    {
        /// <summary>
        ///     宽度
        /// </summary>
        public int Width { get; set; }

        /// <summary>
        ///     高度
        /// </summary>
        public int Height { get; set; }

        /// <summary>
        ///     转换为byte数组
        /// </summary>
        public byte[] ToBytes
        {
            get
            {
                return new[]
                    {
                        (byte) (Width >> 8), (byte) (Width & 0xff),
                        (byte) (Height >> 8), (byte) (Height & 0xff)
                    };
            }
        }
    }

    /// <summary>
    ///     终端类别
    /// </summary>
    public class TerminalTypeWrapper
    {
        private readonly Encoding _encoding = Encoding.ASCII;
        private readonly TerminalType _terminalType = TerminalType.NVT_TTY;

        public TerminalTypeWrapper(TerminalType terminalType, Encoding encoding)
        {
            _terminalType = terminalType;
            _encoding = encoding;
        }

        public byte[] ToBytes
        {
            get
            {
                string terminalTypeString;
                switch (_terminalType)
                {
                   case TerminalType.NVT_VT100:
                        terminalTypeString = "vt100";
                        break;
                case TerminalType.NVT_ANSI:
                        terminalTypeString = "ansi";
                        break;
                    default:
                        terminalTypeString = "tty";
                        break;
                }

                return _encoding.GetBytes(terminalTypeString);
            }
        }
    }

    #endregion
}